ποΈGitΠ―ΡΠ°ποΈ
docs/decisions/navigation3-parity-2026-03.md 20d934459a489ffab53d9dfd88e841ea3df58e6a (20d93445) Text, 10.59 KB
<!--
β’ Copyright (c) 2026 Meshtastic LLC
-
β’ This program is free software: you can redistribute it and/or modify
β’ it under the terms of the GNU General Public License as published by
β’ the Free Software Foundation, either version 3 of the License, or
β’ (at your option) any later version.
-->
Navigation 3 Parity Strategy (Android + Desktop)
Date: 2026-03-11
Status: Implemented (2026-03-21)
Scope: T383838app and T383838desktop navigation structure using shared T383838core:navigation routes
Context
Desktop and Android both use Navigation 3 typed routes from T383838core:navigation. Previously graph wiring had diverged β desktop used a separate T383838DesktopDestination enum with 6 entries (including a top-level Firmware tab) while Android used 5 entries.
This has been resolved. Both shells now use the shared T383838TopLevelDestination enum from T383838core:navigation/commonMain with 5 entries (Conversations, Nodes, Map, Settings, Connections). Firmware is an in-flow route on both platforms.
Both modules still define separate graph-builder files (T383838app/navigation/*.kt, T383838desktop/navigation/*.kt) with different destination coverage and placeholder behavior, but the top-level shell structure is unified.
Current-State Findings
1. Top-level destinations are unified.
β’ Both shells iterate T383838TopLevelDestination.entries from T383838core:navigation/commonMain.
β’ Shared icon mapping lives in T383838core:ui (T383838TopLevelDestinationExt.icon).
β’ Parity tests exist in both T383838core:navigation/commonTest (T383838NavigationParityTest) and T383838desktop/test (T383838DesktopTopLevelDestinationParityTest).
2. Feature coverage is unified via T383838commonMain feature graphs.
β’ The T383838settingsGraph, T383838nodesGraph, T383838contactsGraph, T383838connectionsGraph, T383838firmwareGraph, and T383838mapGraph are now fully shared and exported from their respective feature modules' T383838commonMain source sets.
β’ Desktop acts as a thin shell, delegating directly to these shared graphs.
3. Saved-state route registration is fully shared.
β’ T383838MeshtasticNavSavedStateConfig in T383838core:navigation/commonMain maintains the unified T383838SavedStateConfiguration serializer list.
β’ Both Android and Desktop reference this shared config when instantiating T383838rememberNavBackStack.
4. Predictive back handling is KMP native.
β’ Custom T383838PredictiveBackHandler wrapper was removed in favor of Jetpack's official KMP T383838NavigationBackHandler from T383838androidx.navigationevent:navigationevent-compose.
Alpha04 β Beta01 Changelog Impact Check
Source reviewed: Navigation 3 T3838381.1.0-beta01 (JetBrains fork), CMP T3838381.11.0-beta01, Lifecycle T3838382.11.0-alpha02.
β Superseded by:
β for the full API surface audit and Scene architecture adoption plan.
1. NavDisplay API updated to Scene-based architecture.
β’ The T383838sceneStrategy: SceneStrategy<T> parameter is deprecated in favor of T383838sceneStrategies: List<SceneStrategy<T>>.
β’ New T383838sceneDecoratorStrategies: List<SceneDecoratorStrategy<T>> parameter available.
β’ New T383838sharedTransitionScope: SharedTransitionScope? parameter for shared element transitions.
β’ Existing shell patterns in T383838app and T383838desktop remain valid using the default T383838SinglePaneSceneStrategy.
2. Entry-scoped ViewModel lifecycle adopted.
β’ Both T383838app and T383838desktop now use T383838MeshtasticNavDisplay (T383838core:ui/commonMain), which applies T383838ViewModelStoreNavEntryDecorator + T383838SaveableStateHolderNavEntryDecorator per active backstack.
β’ ViewModels obtained via T383838koinViewModel() inside T383838entry<T> blocks are now scoped to the entry's backstack lifetime.
3. No direct Navigation 3 API breakage.
β’ Release is beta (API stabilized). No migration from alpha04 was required for existing usage patterns.
4. Primary risk is dependency wiring drift, not runtime behavior.
β’ JetBrains Navigation 3 currently publishes T383838navigation3-ui coordinates (no separate T383838navigation3-runtime artifact in Maven Central). The T383838jetbrains-navigation3-runtime alias intentionally points to T383838navigation3-ui and is documented in the version catalog.
β’ Note: The T383838remember* composable factory functions from T383838navigation3-runtime are not visible in non-KMP Android modules due to Kotlin metadata resolution. Use direct class constructors instead (as done in T383838app/Main.kt).
5. Saved-state and typed-route parity improved.
β’ Both hosts share T383838MeshtasticNavSavedStateConfig from T383838core:navigation/commonMain via T383838MultiBackstack, reducing platform drift risk in serializer registration.
6. Updated active docs to reflect the current dependency baseline (T3838381.11.0-beta01, T3838381.1.0-beta01, T3838381.3.0-alpha06, T3838382.11.0-alpha02).
Actions Taken
β’ Renamed all JetBrains-forked lifecycle/nav3 version catalog aliases from T383838androidx-* to T383838jetbrains-* prefix to make fork provenance unambiguous:
β’ T383838jetbrains-lifecycle-runtime, T383838jetbrains-lifecycle-runtime-compose, T383838jetbrains-lifecycle-viewmodel-compose, T383838jetbrains-lifecycle-viewmodel-navigation3
β’ T383838jetbrains-navigation3-runtime, T383838jetbrains-navigation3-ui
β’ Documented in the version catalog that T383838jetbrains-navigation3-runtime intentionally maps to T383838navigation3-ui until a separate runtime artifact is published.
β’ Migrated T383838core:data T383838commonMain from T383838androidx.lifecycle:lifecycle-runtime (Google) to T383838org.jetbrains.androidx.lifecycle:lifecycle-runtime (JetBrains fork) for full consistency.
β’ Updated active docs to reflect the current dependency baseline (T3838381.11.0-beta01, T3838381.1.0-beta01, T3838381.3.0-alpha06, T3838382.11.0-alpha02).
β’ Consolidated T383838app adaptive dependencies to JetBrains Material 3 Adaptive coordinates (T383838org.jetbrains.compose.material3.adaptive:*) so Android and Desktop consume the same adaptive artifact family. The Android-only navigation suite remains on T383838androidx.compose.material3:material3-adaptive-navigation-suite.
Deferred Follow-ups
β’ Add automated validation that desktop serializer registrations stay in sync with shared route keys.
Options Evaluated
Option A: Reuse T383838:app navigation implementation directly in desktop
Pros
β’ Maximum short-term parity in structure.
Cons
β’ T383838:app graph code is tightly coupled to Android wrappers (T383838Android*ViewModel, Android-only screen wrappers, app-specific UI state like scroll-to-top flows).
β’ Pulling this code into desktop would either fail at compile-time or force additional platform branching in app files.
β’ Violates clean module boundaries (T383838desktop should not depend on Android-specific app glue).
Decision: Not recommended.
Option B: Keep fully separate desktop graph and replicate app behavior manually
Pros
β’ Lowest refactor cost right now.
β’ Keeps platform customization simple.
Cons
β’ Drift is guaranteed over time.
β’ No central policy for intentional vs accidental divergence.
β’ High maintenance burden for parity-sensitive flows.
Decision: Not recommended as a long-term strategy.
Option C (Recommended): Hybrid shared contract + platform graph adapters
Pros
β’ Preserves platform-specific wiring where needed.
β’ Reduces drift by moving parity-sensitive definitions to shared contracts.
β’ Enables explicit, testable exceptions for desktop-only or Android-only behavior.
Cons
β’ Requires incremental extraction work.
β’ Needs light governance (parity matrix + tests + docs).
Decision: Recommended.
Decision
Adopt a hybrid parity model:
1. Keep platform graph registration in T383838app and T383838desktop.
2. Extract parity-sensitive navigation metadata into shared contracts (top-level destination set/order, route ownership map, and allowed platform exceptions).
3. Keep platform-specific destination implementations as adapters around shared route keys.
4. Add route parity tests so drift is detected automatically.
Implementation Plan
Phase 1 (Immediate): Stop drift on shell structure β
β’ β
Aligned desktop top-level destination policy with Android (removed Firmware from top-level; kept as in-flow).
β’ β
Both shells now use shared T383838TopLevelDestination enum from T383838core:navigation/commonMain.
β’ β
Shared icon mapping in T383838core:ui (T383838TopLevelDestinationExt.icon).
β’ Parity matrix documented inline: top-level set is Conversations, Nodes, Map, Settings, Connections on both platforms.
Phase 2 (Near-term): Extract shared navigation contracts β
(partially)
β’ β
Shared T383838TopLevelDestination enum with T383838fromNavKey() already serves as the canonical metadata object.
β’ Both T383838app and T383838desktop shells iterate T383838TopLevelDestination.entries β no separate T383838DesktopDestination enum remains.
β’ Remaining: optional visibility flags by platform, route grouping metadata (lower priority since shells are unified).
Phase 3 (Near-term): Add parity checks β
(partially)
β’ β
T383838NavigationParityTest in T383838core:navigation/commonTest β asserts 5 top-level destinations and T383838fromNavKey matching.
β’ β
T383838DesktopTopLevelDestinationParityTest in T383838desktop/test β asserts desktop routes match Android parity set and firmware is not top-level.
β’ Remaining: assert every desktop serializer registration corresponds to an actual route; assert every intentional exception is listed.
Phase 4 (Mid-term): Reduce app-specific graph coupling
β’ Move reusable graph composition helpers out of T383838:app where practical (while keeping Android-only wrappers in Android source sets).
β’ Keep desktop-specific placeholder implementations, but tie them to explicit parity exception entries.
Consequences
β’ Navigation behavior remains platform-adaptive, but parity expectations become explicit and enforceable.
β’ Desktop can keep legitimate deviations (map/charts/platform integrations) without silently changing IA.
β’ New route additions will require touching one shared contract plus platform implementations, making review scope clearer.
Source Anchors
β’ Shared routes: T383838core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt
β’ Shared saved-state config: T383838core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/NavigationConfig.kt
β’ Android shell: T383838app/src/main/kotlin/org/meshtastic/app/ui/Main.kt
β’ Shared graph registrations: T383838feature/*/src/commonMain/kotlin/org/meshtastic/feature/*/navigation/
β’ Platform graph content: T383838feature/*/src/{androidMain,jvmMain}/kotlin/org/meshtastic/feature/*/navigation/
β’ Desktop shell: T383838desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt
β’ Desktop graph assembly: T383838desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt
Served by rngit 1.5.2 - Generated in 0.03s